use std::sync::Arc;

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;

use crate::error::Result;
use crate::prompt::{PromptDefinitionMetadata, PromptManager, PromptTemplate};
use crate::resource::{
    ResourceContent, ResourceDefinition, ResourceDefinitionMetadata, ResourceManager,
};
use crate::tool::{
    DuplicateBehavior, ToolDefinition, ToolDefinitionMetadata, ToolManager, ToolResponse,
};

/// Metadata describing server identity and capabilities.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ServerMetadata {
    pub id: Uuid,
    pub name: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub instructions: Option<String>,
    pub version: String,
    pub created_at: DateTime<Utc>,
}

impl ServerMetadata {
    pub fn new(
        name: impl Into<String>,
        instructions: Option<String>,
        version: impl Into<String>,
    ) -> Self {
        Self {
            id: Uuid::new_v4(),
            name: name.into(),
            instructions,
            version: version.into(),
            created_at: Utc::now(),
        }
    }
}

pub struct FastMcpServer {
    metadata: ServerMetadata,
    tool_manager: ToolManager,
    resource_manager: ResourceManager,
    prompt_manager: PromptManager,
}

impl FastMcpServer {
    pub fn builder() -> FastMcpServerBuilder {
        FastMcpServerBuilder::default()
    }

    pub fn metadata(&self) -> ServerMetadata {
        self.metadata.clone()
    }

    pub fn register_tool(&self, tool: ToolDefinition) -> Result<()> {
        self.tool_manager.register(tool)
    }

    pub fn list_tools(&self) -> Vec<ToolDefinitionMetadata> {
        self.tool_manager.list()
    }

    pub async fn call_tool(&self, name: &str, arguments: Value) -> Result<ToolResponse> {
        self.tool_manager.call(name, arguments).await
    }

    pub fn register_resource(&self, resource: ResourceDefinition) -> Result<()> {
        self.resource_manager.register(resource)
    }

    pub fn list_resources(&self) -> Vec<ResourceDefinitionMetadata> {
        self.resource_manager.list()
    }

    pub async fn read_resource(&self, uri: &str) -> Result<ResourceContent> {
        self.resource_manager.read(uri).await
    }

    pub fn register_prompt(&self, prompt: PromptTemplate) -> Result<()> {
        self.prompt_manager.register(prompt)
    }

    pub fn list_prompts(&self) -> Vec<PromptDefinitionMetadata> {
        self.prompt_manager.list()
    }

    pub fn instantiate_prompt(
        &self,
        name: &str,
        arguments: Option<&Value>,
    ) -> Result<Vec<crate::prompt::PromptMessage>> {
        let prompt = self.prompt_manager.get(name)?;
        prompt.instantiate(arguments)
    }

    pub fn into_shared(self) -> Arc<Self> {
        Arc::new(self)
    }
}

#[derive(Default)]
pub struct FastMcpServerBuilder {
    name: Option<String>,
    instructions: Option<String>,
    version: Option<String>,
    tool_duplicates: DuplicateBehavior,
    resource_duplicates: DuplicateBehavior,
    prompt_duplicates: DuplicateBehavior,
}

impl FastMcpServerBuilder {
    pub fn name(mut self, name: impl Into<String>) -> Self {
        self.name = Some(name.into());
        self
    }

    pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
        self.instructions = Some(instructions.into());
        self
    }

    pub fn version(mut self, version: impl Into<String>) -> Self {
        self.version = Some(version.into());
        self
    }

    pub fn tool_duplicates(mut self, behavior: DuplicateBehavior) -> Self {
        self.tool_duplicates = behavior;
        self
    }

    pub fn resource_duplicates(mut self, behavior: DuplicateBehavior) -> Self {
        self.resource_duplicates = behavior;
        self
    }

    pub fn prompt_duplicates(mut self, behavior: DuplicateBehavior) -> Self {
        self.prompt_duplicates = behavior;
        self
    }

    pub fn build(self) -> FastMcpServer {
        let name = self
            .name
            .unwrap_or_else(|| format!("fastmcp-{}", Uuid::new_v4()));
        let version = self.version.unwrap_or_else(|| "0.1.0-preview".into());
        let server = FastMcpServer {
            metadata: ServerMetadata::new(name, self.instructions, version),
            tool_manager: ToolManager::new(self.tool_duplicates),
            resource_manager: ResourceManager::new(self.resource_duplicates),
            prompt_manager: PromptManager::new(self.prompt_duplicates),
        };

        #[cfg(feature = "auto-register")]
        {
            crate::tool::register_discovered_tools(&server);
        }

        server
    }
}
